# This code is so you can run the samples without installing the package
import sys
import os
from compiler.ast import Node
#from IPython.utils.traitlets import NoDefaultSpecified
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))

# python
import math

# stdlib
import copy
import random
import weakref

# pyglet related
import pyglet
from pyglet.window import key

# cocos2d related
import cocos
from cocos.euclid import Point2
from cocos import sprite
from cocos.tiles import *

# testrico related
from constants import *

from utils import *

__all__ = ['GameModel']

#
# Model (of the MVC pattern)
#

class GameModel( pyglet.event.EventDispatcher ):

    def __init__(self):
        super(GameModel,self).__init__()
    
        self.bicho = Pacman('fantasma.png')

        
        #cargo el mapa para las colisiones
        #self.tmx_resource = load_tmx('test_collisions.tmx')
        self.tmx_resource = load_tmx('niveles.tmx')
        # levanto las capas del tmx
        self.nivel_1     = self.tmx_resource['nivel_1']
        self.bicho.set_parent_map(self.nivel_1)
        #self.base_map        = self.tmx_resource['base']
        #self.esquinas        = self.tmx_resource['esquinas']
        #self.items        = self.tmx_resource['items']
        # la capa pastillas contiene las pastillitas con el attributo
        # pastilla = 1        
        self.pasti_map        = self.tmx_resource['pastillas']
        
        self.astar_board = tiledmap2board(self.nivel_1, BOARD_START_POINT, BOARD_END_POINT, 'wall')
        
        self.key_pressed = None
        self.cant_pills = 0 

        self.sombra = Sombra('sombra.png')
        self.sombra.set_parent_map(self.nivel_1)
        self.sombra.init_parent_map()

        self.rapido = Rapido('rapido.png')
        self.rapido.set_parent_map(self.nivel_1)
        self.rapido.init_parent_map()

        self.tonto = Tonto('tonto.png')
        self.tonto.set_parent_map(self.nivel_1)
        self.tonto.init_parent_map()

        self.miedoso = Miedoso('miedoso.png')
        self.miedoso.set_parent_map(self.nivel_1)
        self.miedoso.init_parent_map()
        self.miedoso.set_brother(self.sombra)

        self.libre=True
        
        # self.pMatrix = [[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
        #                 [0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
        #                 [0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0],
        #                 [0,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,0],
        #                 [0,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0,0],
        #                 [0,3,1,0,1,1,1,1,1,2,1,1,1,1,1,0,1,3,0],
        #                 [0,1,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0],
        #                 [0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0],
        #                 [0,0,0,0,1,0,2,0,0,0,0,0,2,0,1,0,0,0,0],
        #                 [0,0,0,0,1,0,2,2,2,2,2,2,2,0,1,0,0,0,0],
        #                 [0,0,0,0,1,0,2,0,0,0,0,0,2,0,1,0,0,0,0],
        #                 [4,2,2,2,1,2,2,0,2,2,2,0,2,2,1,2,2,2,4],
        #                 [0,0,0,0,1,0,2,0,0,5,0,0,2,0,1,0,0,0,0],
        #                 [0,0,0,0,1,0,2,2,2,2,2,2,2,0,1,0,0,0,0],
        #                 [0,0,0,0,1,0,0,0,2,0,2,0,0,0,1,0,0,0,0],
        #                 [0,1,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,0],
        #                 [0,1,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,1,0],
        #                 [0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
        #                 [0,1,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0],
        #                 [0,3,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,3,0],
        #                 [0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0],
        #                 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]] 
    
#         self.smallPellets = ThingsContainer()
#     
#         for x in range(18):
#             for y in range(21):
#                 if self.pMatrix[y][x] == 1:
#                     smallPellet= SmallPellet('pellet.png')
#                     smallPellet.scale=0.5
#                     smallPellet.position= Point2((192+(x*TILE_SIZE))+HALF_TILE_SIZE,(TILE_SIZE+(y*TILE_SIZE))+HALF_TILE_SIZE)
#                     self.smallPellets.add(smallPellet)

    def set_controller( self, ctrl ):
        self.ctrl = weakref.ref( ctrl )

    def catch_key(self, k):
        self.bicho.pressed=True
        if self.bicho.doblando():
            self.key_pressed=k
        else:
            self.check_key(k)

    def release_key(self, k):
        self.bicho.pressed=False


    def check_key(self, k):
        if k == key.LEFT:
            self.bicho_left()
        elif k == key.RIGHT:
            self.bicho_right()
        elif k == key.DOWN:
            self.bicho_down()
        elif k == key.UP:
            self.bicho_up()


    def bicho_right( self ):
        '''moves the bicho to laderecha'''
        self.bicho.dir_prop = Point2(1, 0)
        self.bicho.dir_prop_copy=self.bicho.dir_prop
        self.dispatch_event("on_move_bicho")

    def bicho_left( self ):
        '''moves the bicho to laizquierda'''
        self.bicho.dir_prop = Point2(-1, 0)
        self.bicho.dir_prop_copy=self.bicho.dir_prop
        self.dispatch_event("on_move_bicho")

    def bicho_down( self ):
        '''moves the bicho to laabajo'''
        self.bicho.dir_prop = Point2(0, -1)
        self.bicho.dir_prop_copy=self.bicho.dir_prop
        self.dispatch_event("on_move_bicho")

    def bicho_up( self ):
        '''moves the bicho to laarriba'''
        self.bicho.dir_prop = Point2(0, 1)
        self.bicho.dir_prop_copy=self.bicho.dir_prop
        self.dispatch_event("on_move_bicho")

    def set_prop_direction(self,k):
        self.check_key(k) 

    # def bicho_stop( self ):
    #     '''stops the bug!'''
    #     self.bicho.vel = Point2(0, 0)
    #     self.dispatch_event("on_stop_bicho")

    # def bicho_turn_right( self ):
    #     current_cell = self.esquinas.get_at_pixel(self.bicho.x,self.bicho.y)
    #     c = current_cell.get

    #     if c('corner'):
    #         if self.bicho.y == current_cell.center[1]:
    #             self.bicho.vel = Point2(self.bicho.speed, 0)
    #             self.dispatch_event("on_move_bicho")

    # def bicho_turn_left( self ):
    #     current_cell = self.esquinas.get_at_pixel(self.bicho.x,self.bicho.y)
    #     c = current_cell.get

    #     if c('corner'):
    #         if self.bicho.y == current_cell.center[1]:
    #             self.bicho.vel = Point2(-self.bicho.speed, 0)
    #             self.dispatch_event("on_move_bicho")

    # def bicho_turn_up( self ):
    #     current_cell = self.esquinas.get_at_pixel(self.bicho.x,self.bicho.y)
    #     c = current_cell.get

    #     if c('corner'):
    #         if self.bicho.x == current_cell.center[0]:
    #             self.bicho.vel = Point2(0, self.bicho.speed)
    #             self.dispatch_event("on_move_bicho")

    # def bicho_turn_down( self ):
    #     current_cell = self.esquinas.get_at_pixel(self.bicho.x,self.bicho.y)
    #     c = current_cell.get

    #     if c('corner'):
    #         if self.bicho.x == current_cell.center[0]:
    #             self.bicho.vel = Point2(0, -self.bicho.speed)
    #             self.dispatch_event("on_move_bicho")

    def playerAgarrado(self, tile_map):
        bichoCell = tile_map.get_at_pixel(self.bicho.x,self.bicho.y)
        #rapidoCell = tile_map.get_at_pixel(self.rapido.x,self.rapido.y)
        sombraCell = tile_map.get_at_pixel(self.sombra.x,self.sombra.y)
        #tontoCell = tile_map.get_at_pixel(self.tonto.x,self.tonto.y)
        #miedosoCell = tile_map.get_at_pixel(self.miedoso.x,self.miedoso.y)
        
        if bichoCell == sombraCell:# or rapidoCell == sombraCell or bichoCell == tontoCell or miedosoCell == bichoCell:
            return True
        return False

    def pillAgarrada(self, pasti_map):
        bichoCell = pasti_map.get_at_pixel(self.bicho.x,self.bicho.y)
        celda = pasti_map.get_cell(bichoCell.i, bichoCell.j)
        if celda.get('active'):
            #print "Hay pasti"
            if celda.__getitem__('active') == 1:
                self.cant_pills+=1
                celda.__setitem__('active', 0)
                #print self.cant_pills
                #celda.active=0
                
        
    def update(self):
        if self.libre:
            if (not self.bicho.doblando() and self.key_pressed):
                self.set_prop_direction(self.key_pressed)
                self.key_pressed = None
            self.bicho.update()
            self.pillAgarrada(self.pasti_map)
            #self.sombra.set_target(Point2(self.bicho.x, self.bicho.y))
            self.sombra.setChaseTarget(self.bicho)
            self.sombra.update()
            self.tonto.setChaseTarget(self.bicho)
            self.tonto.update()
            self.miedoso.setChaseTarget(self.bicho)
            self.miedoso.update()
            self.rapido.setChaseTarget(self.bicho)
            self.rapido.update()
            
            if self.playerAgarrado(self.nivel_1):
                print 'agarrado'
                self.libre=False
        

class Pellet( sprite.Sprite ):
    def __init__(self, image):
        super( Pellet, self).__init__(image)

class SmallPellet( Pellet ):
    def __init__(self, image):
        super( SmallPellet, self).__init__(image)

class BigPellet( Pellet ):
    def __init__(self, image):
        super( BigPellet, self).__init__(image)



class Bicho( sprite.Sprite ):
    '''Base class for all bichos'''
    def __init__(self, image):
        super( Bicho, self).__init__(image)

        self.parent_map = None
        self.board = None

    def update(self):
        self.checkTeleportCell()
        self.set_direction()
        self.set_move()
        if self.pressed:
            self.dir_prop = self.dir_prop_copy

    def get_target_cell(self):
        if self.target:
            return self.parent_map.get_at_pixel(self.target.x, self.target.y)
        else:
            return None
    
    def get_current_cell(self):
        return self.parent_map.get_at_pixel(self.x,self.y)

    def get_next_cell(self):
        current_cell = self.get_current_cell()
        #print self.dir_prop.x
        return self.parent_map.get_cell(current_cell.i + self.dir_prop.x, current_cell.j + self.dir_prop.y)

    def get_next_cell_dir(self):
        current_cell = self.get_current_cell()
        #print self.dir_prop.x
        return self.parent_map.get_cell(current_cell.i + self.dir.x, current_cell.j + self.dir.y)
    
    def set_target(self, target):
        '''' Set target '''
        self.target = target

    def set_parent_map(self, mapa):
        '''' Set target '''
        self.parent_map = mapa
        self.board = tiledmap2board(self.parent_map, BOARD_START_POINT, BOARD_END_POINT, 'wall')
    
    def init_parent_map(self):
        self.cell = self.get_current_cell()
        self.current_cell = [self.cell.i,self.cell.j]
        self.next_cell = self.current_cell

    def checkTeleportCell(self):
        c = self.get_current_cell()
        t = c.get('teleport')
        if t:
            if c.i == 5:
                self.x = 24*TILE_SIZE
            else:
                self.x = 6*TILE_SIZE
                

            
            

class Enemy( Bicho ):
    ''' '''
    def __init__(self, image):
        super(Enemy, self).__init__(image)
        
        self.speed=2
        self.dirActual=None
        self.dirSiguiente=None
        self.x = (15*TILE_SIZE)+HALF_TILE_SIZE
        self.y = (14*TILE_SIZE)+HALF_TILE_SIZE
        self.vel = Point2(0,0)
        #self.cell = self.get_current_cell()
        #self.current_cell = [self.cell.i,self.cell.j]
        #self.next_cell = self.current_cell
        self.target = None
    
  
    def update(self):
        self.checkTeleportCell()
        self.cell = self.get_current_cell()
        self.current_cell = [self.cell.i, self.cell.j]
        
        if self.current_cell == self.next_cell:
            if not self.dirActual:
                forbiden_position = None
            else:
                forbiden_position = [self.cell.i - self.dirActual.x, self.cell.j - self.dirActual.y]
            target_cell = self.get_target_cell()
            #target_cell = map_tile.get_at_pixel(self.target)
            path = a_star(self.cell, target_cell, self.board, forbiden_position)
            if path.__len__() > 1:
                nodo = path[1]
            else:
                nodo = self.getNode(forbiden_position)

            self.next_cell = [nodo.x, nodo.y]
            self.dirSiguiente = Point2(self.next_cell[0] - self.current_cell[0], self.next_cell[1] - self.current_cell[1])
            if not self.dirActual:
                self.dirActual=Point2(self.dirSiguiente.x,self.dirSiguiente.y)
        if self.dirActual.x != 0:
            dist= abs(self.cell.center[0] - self.x)
        else:
            dist= abs(self.cell.center[1] - self.y)
        if(self.dirActual.dot(self.dirSiguiente) == 0 and self.speed >= dist):
            # si las direcciones son perpendiculares
            self.dirActual = self.dirSiguiente
            self.x = self.cell.center[0]
            self.y = self.cell.center[1]
            resto = self.speed - dist
            self.x += resto * self.dirActual.x
            self.y += resto * self.dirActual.y
        else:
            self.x += self.speed * self.dirActual.x
            self.y += self.speed * self.dirActual.y
        
        
    def getNode(self, forbidden):
        adjacents = [(self.cell.i-1, self.cell.j),(self.cell.i+1, self.cell.j),(self.cell.i, self.cell.j+1),(self.cell.i, self.cell.j-1)]
        for neighbor in adjacents:
            if forbidden: 
                if neighbor[0] == forbidden[0] and neighbor[1] == forbidden[1]:
                    continue 
            celda = self.parent_map.get_cell(neighbor[0], neighbor[1])    
            if(not celda.get('wall')):
                return Node(celda.i, celda.j, -1, -1, -1, -1)
        print 'no retorno nada'

class Sombra(Enemy):
    def __init__(self, image):
        super( Sombra, self).__init__(image)
        self.speed = 2.4
        self.x = (15*TILE_SIZE)+HALF_TILE_SIZE
        self.y = (14*TILE_SIZE)+HALF_TILE_SIZE

    def setChaseTarget(self, player):
        self.set_target(Point2(player.x, player.y))
        
class Tonto(Enemy):
    def __init__(self, image):
        super( Tonto, self).__init__(image)
        self.speed = 2.5
        self.x = (15*TILE_SIZE)+HALF_TILE_SIZE
        self.y = (14*TILE_SIZE)+HALF_TILE_SIZE

    def setChaseTarget(self, player):
        tontoPlayerDist2 = (player.x - self.x)*(player.x - self.x) + (player.y - self.y)*(player.y - self.y) 
        if tontoPlayerDist2 > 64*TILE_SIZE*TILE_SIZE:
            self.set_target(Point2(player.x, player.y))
        else:
            self.set_target(target_orange)


class Rapido(Enemy):
    def __init__(self, image):
        super( Rapido, self).__init__(image)
        self.speed = 2.1
        self.x = (15*TILE_SIZE)+HALF_TILE_SIZE
        self.y = (14*TILE_SIZE)+HALF_TILE_SIZE

    def setChaseTarget(self, player):
        original_target = [player.x + 4*TILE_SIZE*player.dir.x, player.y + 4*TILE_SIZE*player.dir.y]
        
        if original_target[0] < 0:
            original_target[0] = 0
        else:
            original_target[0] = min(original_target[0], 31*TILE_SIZE)
            
        if original_target[1] < 0:
            original_target[1] = 0
        else:
            original_target[1] = min(original_target[1], 23*TILE_SIZE)
            
        vcell_pos = validateDestinationOutside(original_target, self.parent_map)
        valid_target = validateDestinationInside(vcell_pos, self.parent_map)
        self.set_target(Point2(TILE_SIZE*valid_target[0], TILE_SIZE*valid_target[1]))

class Miedoso(Enemy):
    def __init__(self, image):
        super( Miedoso, self).__init__(image)
        self.speed = 2.5
        self.x = (15*TILE_SIZE)+HALF_TILE_SIZE
        self.y = (14*TILE_SIZE)+HALF_TILE_SIZE
        self.brother = None

    def setChaseTarget(self, player):
        auxiliar_target = [player.x + 2*TILE_SIZE*player.dir.x, player.y + 2*TILE_SIZE*player.dir.y]
        original_target = [2*auxiliar_target[0]-self.brother.x, 2*auxiliar_target[1]-self.brother.y]
        
        if original_target[0] < 0:
            original_target[0] = 0
        else:
            original_target[0] = min(original_target[0], 31*TILE_SIZE)
            
        if original_target[1] < 0:
            original_target[1] = 0
        else:
            original_target[1] = min(original_target[1], 23*TILE_SIZE)
            
            
        vcell_pos = validateDestinationOutside(original_target, self.parent_map)
        valid_target = validateDestinationInside(vcell_pos, self.parent_map)
        self.set_target(Point2(TILE_SIZE*valid_target[0], TILE_SIZE*valid_target[1]))

    def set_brother(self, bro):
        self.brother = bro

class Pacman( Bicho ):
    def __init__(self, image):
        super( Pacman, self).__init__(image)

        self.pressed=False
        self.dir_prop_copy=None
        self.speed = 2.9
        self.x = (15*TILE_SIZE)+HALF_TILE_SIZE
        self.y = (6*TILE_SIZE)+HALF_TILE_SIZE

        # Aca mejor defino la velocidad como un vector de 2 componentes
        self.vel = Point2(-self.speed, 0)
        self.dir = Point2(-1,0)
        self.dir_prop = Point2(-1,0)

    def doblando(self):
        return (self.vel.x != 0 and self.vel.y !=0)
         
    

    def set_sentido(self):
        # yendo horizontal
        current_cell = self.get_current_cell()
        if self.dir.x != 0:
            dist = current_cell.center[0] - self.x
            if dist > 0:
                if self.dir.x < 0:
                    self.dir.x = self.dir.x * -1
                    self.vel.x = self.speed * self.dir.x
            elif dist < 0:
                if self.dir.x > 0:
                    self.dir.x = self.dir.x * -1
                    self.vel.x = self.speed * self.dir.x
            self.dir.y = self.dir_prop.y
        elif self.dir.y != 0:
            dist = current_cell.center[1] - self.y
            if dist > 0:
                if self.dir.y < 0:
                    self.dir.y = self.dir.y * -1
                    self.vel.y = self.speed * self.dir.y
            elif dist < 0:
                if self.dir.y > 0:
                    self.dir.y = self.dir.y * -1
                    self.vel.y = self.speed * self.dir.y
            self.dir.x = self.dir_prop.x


    def set_direction(self):
        
        next_cell = self.get_next_cell()
        if not (self.dir_prop.y == self.dir.y and self.dir_prop.x == self.dir.x) and not self.doblando():
            #print next_cell.i,next_cell.j,next_cell.get('wall')
            if(not next_cell.get('wall')):
                if(self.dir.dot(self.dir_prop) == 0):
                    # estamos doblando, direccion propuesta es perpendicular a la actual
                    self.set_sentido()
                    self.vel.x += self.speed * self.dir_prop.x
                    self.vel.y += self.speed * self.dir_prop.y
                else:
                    self.dir = self.dir_prop                    
                    self.vel.x = self.speed * self.dir.x
                    self.vel.y = self.speed * self.dir.y
            else:
                self.dir_prop=self.dir

    def set_move(self):
        
        current_cell = self.get_current_cell()
        next_cell= self.get_next_cell()
        if(self.dir.x != 0 and self.dir.y !=0):
        # si vamos en diagonal, o sea doblando
            if (self.dir_prop.y != 0):
                # si dir_pror es arriba o abajo, vertical, frenamos la x
                distx = abs(current_cell.center[0] - self.x)
                if(distx < self.speed):
                    self.x = current_cell.center[0]
                    self.vel.x = 0
                
            else:
                # si dir_prop es izquierda o derecha, horizontal, frenamos la y
                disty = abs(current_cell.center[1] - self.y)
                if(disty < self.speed):
                    self.y = current_cell.center[1]
                    self.vel.y = 0
        else:
            # si vamos en horizontal o vertical
            if(next_cell.get('wall')):
                distx = abs(current_cell.center[0] - self.x)
                disty = abs(current_cell.center[1] - self.y)
                if(distx < self.speed):
                    self.x = current_cell.center[0]
                    self.vel.x = 0
                if(disty < self.speed):
                    self.y = current_cell.center[1]
                    self.vel.y = 0
        self.position = (self.x + self.vel.x,self.y + self.vel.y)


# esta clase derivada de BatchableNode, agrega los metodos para updatear
# y dibujar a cada uno de los children.
# Asume que los children tienen los metodos draw y update

class ThingsContainer(cocos.batch.BatchableNode):
    def __init__( self ):
        super( ThingsContainer, self ).__init__()

    def update(self):
        for t in self.get_children():
            t.update()

    def draw(self):
        for t in self.get_children():
            t.draw()

GameModel.register_event_type('on_move_bicho')
GameModel.register_event_type('on_stop_bicho')